#include "MysqlConnPool.h"
#include "exception/SQLException.h"
#include <thread>

using namespace std;
using namespace mysql;

MysqlConnPool::MysqlConnPool(int min_conn_count, int max_conn_count,
                             const string &ip, int port,
                             const string &user, const string &password,
                             const string &dbname, const string &character_set) :
        min_conn_count_(min_conn_count),
        max_conn_count_(max_conn_count),
        ip_(ip), port_(port),
        user_(user), password_(password),
        dbname_(dbname), character_set_(character_set) {
    for (int i = 0; i < min_conn_count_; ++i) {
        addConnection();
    }
}

MysqlConnPool::~MysqlConnPool() {
    stop_ = true;
    while (true) {
        queue_locker_.lock();
        if (conn_queue_.size() == alive_conn_count_)
            break;
        queue_locker_.unlock();
        this_thread::sleep_for(chrono::milliseconds(10));
    }
    while (!conn_queue_.empty()) {
        MysqlConn *conn = conn_queue_.front();
        delete conn;
        conn_queue_.pop();
    }
}

shared_ptr<MysqlConn> MysqlConnPool::getConnection(bool is_create) {
    if (stop_) throw runtime_error("Get connection after pool destory");
    unique_lock<mutex> lck(queue_locker_);
    if (is_create && conn_queue_.empty() && alive_conn_count_ < max_conn_count_) {
        addConnection();
    }
    variable_.wait(lck, [this]() { return !conn_queue_.empty(); });
    shared_ptr<MysqlConn> ret(conn_queue_.front(), [this](MysqlConn *conn) {
        queue_locker_.lock();
        conn_queue_.push(conn);
        queue_locker_.unlock();
        variable_.notify_one();
    });
    conn_queue_.pop();
    return ret;
}

void MysqlConnPool::addConnection() {
    auto *conn = new MysqlConn;
    conn->connect(user_, password_, dbname_, ip_, port_);
    conn->setCharacterSet(character_set_);
    conn_queue_.push(conn);
    ++alive_conn_count_;
}

bool MysqlConnPool::release() {
    queue_locker_.lock();
    if (alive_conn_count_ > min_conn_count_ && !conn_queue_.empty()) {
        MysqlConn *conn = conn_queue_.front();
        delete conn;
        conn_queue_.pop();
        queue_locker_.unlock();
        return true;
    }
    queue_locker_.unlock();
    return false;
}
